﻿#region Copyright
//       Copyright (c) 2009 - Antoine Jaussoin - http://www.jaussoin.com/blog/
// 
//        This program is free software: you can redistribute it and/or modify
//        it under the terms of the GNU General Public License as published by
//        the Free Software Foundation, either version 3 of the License, or
//        (at your option) any later version.
// 
//        This program is distributed in the hope that it will be useful,
//        but WITHOUT ANY WARRANTY; without even the implied warranty of
//        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//        GNU General Public License for more details.
// 
//        You should have received a copy of the GNU General Public License
//        along with this program.  If not, see <http://www.gnu.org/licenses/>.
#endregion
using System;
using System.Collections.Generic;
using System.Reflection;
using FT.Architecture.Utils.ExtensionMethods;

namespace FT.Architecture.Utils.Reflection
{
    /// <summary>
    /// Help with attributes
    /// </summary>
    public class Attributes
    {
        /// <summary>
        /// Get all the properties of a given object which are decorated with the given attribute
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static IList<PropertyInfo> GetPropertiesWithAttribute<T>(object obj) where T:Attribute
        {
            return GetPropertiesWithAttribute<T>(obj, false);
        }

        /// <summary>
        /// Get all the properties of a given object which are decorated with the given attribute.
        /// If inherited is set to true, then any property at any level of the object hierarchy will be considered.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static IList<PropertyInfo> GetPropertiesWithAttribute<T>(object obj, bool inherited) where T : Attribute
        {
            Type type = obj.GetType();
            return GetPropertiesWithAttribute<T>(type, inherited);
        }

        private static IList<PropertyInfo> GetPropertiesWithAttribute<T>(Type type, bool inherited) where T : Attribute
        {
            var properties = new List<PropertyInfo>();

            BindingFlags flags = !inherited
                 ? BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
                 : BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

            object[] props = type.GetProperties(flags);

            foreach (PropertyInfo property in props)
            {
                object[] attributes = property.GetCustomAttributes(typeof(T), inherited);
                if (attributes.Length > 0)
                {
                    properties.Add(property);
                }
            }

            if (inherited)
            {
                foreach (Type interf in type.GetInterfaces())
                {
                    properties.AddRange(GetPropertiesWithAttribute<T>(interf, true));
                }

                if (type.BaseType != null && type.BaseType != typeof (object))
                {
                    properties.AddRange(GetPropertiesWithAttribute<T>(type.BaseType, true));
                }
            }

            return properties.RemoveDuplicates();
        }
    }
}
